import threading
import time
import select
import socket
import packet
import sys
import server_select
import urllib
import os
import shutil


class Server:   	
	class Client_list:	
		def __init__ (self):
			self.list_conn = []
		
		def add_conn(self, name, socket):
			self.list_conn.append((name, socket))
			return True
		
		def get_name(self, socket):
			for tup_crt in self.list_conn:
				str_name, sock = tup_crt
				if socket is sock:
					return str_name
			return None
		
		def get_sock(self, name):
			for tup_crt in self.list_conn:
				str_name, sock = tup_crt
				if str_name == name:
					return sock
			return None
		
		def remove_sock(self, sock):
			str_name = self.get_name(sock)
			if (str_name is not None):
				list_tmp = self.list_conn[:]
				self.list_conn = []
				for str_name_crt, sock_crt in list_tmp:
					if sock_crt != sock:
						self.list_conn.append((str_name_crt, sock_crt))
		
		def get_list(self):
			list_client = []
			for tup_crt in self.list_conn:
				str_name, sock = tup_crt
				list_client.append(str_name)
			
			list_client.sort()
			return list_client


	def __init__(self, filename, password, verbose):		
		self.str_presentation	= self.read_presentation(filename)
		self.str_password		= password;
		
		self.bool_running		= False
		self.int_buffersize 	= 1024
		self.int_current_slide	= 0
		self.int_sent_slide		= 0
		self.verbose			= verbose
		self.client_list		= self.Client_list()
		self.list_downloaded	= []
		self.bool_ok			= True
	
	def connect(self,host,port):
		try:
			# create socket and listen
			self.sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			self.sock_server.bind((host,port))
			self.sock_server.listen(5)
			# create select read list
			self.list_input_sock=[self.sock_server]
			# start select thread
			self.bool_running=True
			self.servst_loop=server_select.Server_select_thread(self, self.verbose)
			self.servst_loop.start()		
			if self.verbose: print "OpenCDS Server online."
			self.bool_ok			= True
		except:
			if self.verbose: print "Server could not be started."
			self.bool_ok			= False

		
	# ****************************************
	#  IMPORTANT : PACKET ACTION OCCURS HERE!
	# ****************************************
	def parse_packet(self, tup_packet, sock_current):
		str_type, var_data = tup_packet
		
		if str_type == 'auth':
			#authentication packet
			str_user, str_pass = var_data
				
			if str_pass == self.str_password:
				# check for duplicate users/ duplicate authentications
				sock_chk = self.client_list.get_sock(str_user)
				str_chk= self.client_list.get_name(sock_current)
				
				if (sock_chk is not None):
					if str_user != str_chk:
						if self.verbose: print "SERVER: Duplicate name detected. Resolved."
						str_prespack = packet.encode_server_packet('ares', (False, "Another user with the same name is already connected."))
						if ( str_prespack is not None ):
							sock_current.send(str_prespack)
						self.list_input_sock.remove(sock_current)
						sock_current.close()
					else:
						if self.verbose: print "SERVER: Duplicate authentication. Ignored."
				elif (str_chk is not None):
					if self.verbose: print "SERVER: Duplicate authentication. Ignored."
				else:				
					# user authenticated
					self.client_list.add_conn(str_user, sock_current)
					str_prespack = packet.encode_server_packet('ares', (True, "Welcome to OpenCDs."))
					if ( str_prespack is not None ):
						sock_current.send(str_prespack)
						if self.verbose: print "SERVER: Authentication for user",str_user,"successful..."
					str_prespack = packet.encode_server_packet('pxml', self.str_presentation)
					if ( str_prespack is not None ):
						sock_current.send(str_prespack)
						if self.verbose: print "SERVER: XML presentation sent..."
					str_prespack = packet.encode_server_packet('psno', self.int_current_slide)
					if ( str_prespack != None ):
						sock_current.send(str_prespack)
						if self.verbose: print "SERVER: Presentation slide number sent..."
			else:
				str_prespack = packet.encode_server_packet('ares', (False, "Incorrect password."))
				if ( str_prespack != None ):
					sock_current.send(str_prespack)
					if self.verbose: print "SERVER: Authentication for user",str_user,"failed..."
				self.list_input_sock.remove(sock_current)
				self.client_list.remove_sock(sock_current)
				sock_current.close()
			
		elif str_type == 'internal-error':
			str_user = self.client_list.get_name(sock_current)
			
			if self.verbose: 
				if str_user is not None:
					print "SERVER: User",str_user,"has quit. Connection reports:",var_data
				
				if sock_current in self.list_input_sock:
					self.list_input_sock.remove(sock_current)
						
				self.client_list.remove_sock(sock_current)
				sock_current.close()
	
	def set_current_slide(self, int_slide):
		self.int_current_slide = int_slide

	def get_current_slide(self):
		return self.int_current_slide
	
	def get_user_list(self):
		return self.client_list.get_list()
	
	def get_listen_list(self):
		return self.list_input_sock

	def stop(self):
		self.bool_running=False
	
	def kick_user(self, str_user):
		sock_chk = self.client_list.get_sock(str_user)
		
		if (sock_chk is not None):
			if self.verbose: print "SERVER: Kicking user:",str_user
			str_prespack = packet.encode_server_packet('ares', (False, "You've been disconnected."))
			if ( str_prespack != None ):
				sock_chk.send(str_prespack)
			if sock_chk in self.list_input_sock:
				self.list_input_sock.remove(sock_chk)
			self.client_list.remove_sock(sock_chk)
			sock_chk.close()
	
	def page_user(self, str_user, str_mesg):
		str_prespack = packet.encode_server_packet('mesg', str_mesg)
		
		if str_user!="*ALL*":
			sock_chk = self.client_list.get_sock(str_user)
			if sock_chk is not None:
				if str_prespack != None:
					sock_chk.send(str_prespack)
		else:
			for sock in self.list_input_sock:
				if ((sock is not sys.stdin) and (sock is not self.sock_server)):
					sock.send(str_prespack)

	
	def read_presentation(self, filename):
		try:
			f = open(filename)
			try:
				return f.read()
			except IOError:
				#return an empty presentation
				return "<presentation/>"
			else:
				f.close()
		except IOError:
			#return an empty presentation
			return "<presentation/>"

	def get_image(self,filename,url):
		# create the directory for download
		
		try:
			os.mkdir('/tmp/'+str(os.getpid()))
		except OSError:
			pass
		
		str_current = '/tmp/'+str(os.getpid())+"/"+filename
		
		for tup_elem in self.list_downloaded:
			str_url, str_file = tup_elem
			
			if str_url == url:
				# already downloaded ...
				if str_file == str_current:
					# ... to the correct filename
					return str_current
				else:
					# ... to another file
					try:
						shutil.copyfile(str_file, str_current)
						self.list_downloaded.append((url,str_current))
					except:
						return None
					return str_current
			
		# we need to download this file
		try:
			print 'Downloading:',url,'to',str_current
			urllib.urlretrieve(url, str_current)
		except:
			print "Try to download failed"
			return None
		else:
			self.list_downloaded.append((url,str_current))
			return str_current		
	
	
	def cleanup_images(self):
		try:
			for tup_elem in self.list_downloaded[:]:
				str_url, str_file = tup_elem
				# try to delete file
				try:
					self.list_downloaded.remove(tup_elem)
					os.remove(str_file)
				except:
					pass
			os.rmdir('/tmp/'+str(os.getpid()))
		except OSError:
			pass
	
	
